package com.nulldev.util.security.encryptAPI_rw.impls.aes;

import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import com.nulldev.util.io.IOUtils;
import com.nulldev.util.security.encryptAPI_rw.api.ICipherType;
import com.nulldev.util.security.encryptAPI_rw.api.IStandardBlockingCipher;
import com.nulldev.util.security.encryptAPI_rw.api.IStandardStreamingCipher;

public class AESECBCipher implements IStandardBlockingCipher, IStandardStreamingCipher {

	private static final String algorithm = "AES/ECB/NoPadding";
	private static final String[] CIPHER_NAMES =
		{ "AES", "AES-ECB", "ECB" };

	public AESECBCipher() {
		try {
			Cipher.getInstance(algorithm);
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}

	@Override
	public String getEncryptorID() {
		return "ecr_v3.jre.aes-ecb";
	}

	@Override
	public String[] getCipherNames() {
		return CIPHER_NAMES;
	}

	@Override
	public long encrypt(final InputStream is, final OutputStream os, final byte[] privateKey) {
		final Key keySpec = new SecretKeySpec(privateKey, "AES");

		try {
			final Cipher cipher = Cipher.getInstance(algorithm);
			cipher.init(Cipher.ENCRYPT_MODE, keySpec);
			{
				final byte[] buffer = new byte[IOUtils.MEMORY_ALLOC_BUFFER];
				long count = 0L;
				int n = 0;
				while (-1 != (n = is.read(buffer))) {
					os.write(cipher.update(buffer), 0, n);
					count += n;
				}
				os.write(cipher.doFinal());
				return count;
			}
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}

	@Override
	public long decrypt(final InputStream is, final OutputStream os, final byte[] privateKey) {
		final Key keySpec = new SecretKeySpec(privateKey, "AES");

		try {
			final Cipher cipher = Cipher.getInstance(algorithm);
			cipher.init(Cipher.DECRYPT_MODE, keySpec);
			{
				final byte[] buffer = new byte[IOUtils.MEMORY_ALLOC_BUFFER];
				long count = 0L;
				int n = 0;
				while (-1 != (n = is.read(buffer))) {
					os.write(cipher.update(buffer), 0, n);
					count += n;
				}
				os.write(cipher.doFinal());
				return count;
			}
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}

	@Override
	public byte[] encrypt(final byte[] data, final byte[] key) {
		final Key keySpec = new SecretKeySpec(key, "AES");

		try {
			final Cipher cipher = Cipher.getInstance(algorithm);
			cipher.init(Cipher.ENCRYPT_MODE, keySpec);
			return cipher.doFinal(data);
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}

	@Override
	public byte[] decrypt(final byte[] data, final byte[] key) {
		final Key keySpec = new SecretKeySpec(key, "AES");

		try {
			final Cipher cipher = Cipher.getInstance(algorithm);
			cipher.init(Cipher.DECRYPT_MODE, keySpec);
			return cipher.doFinal(data);
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}

	@Override
	public ICipherType getCipherType() {
		return ICipherType.PRIVATE_KEY;
	}
}
